7.2: Connect to the Internet
Contents:
- Introduction
- Network security
- Including permissions in the manifest
- Performing network operations on a worker thread
- Making an HTTP connection
- Parsing the results
- Managing the network state
- Related practical
- Learn more
Most Android applications have some data that the user interacts with; it might be news articles, weather information, contacts, game data, user information, and more. Often, this data is provided over the network by a web API.
In this lesson you learn about network security and how to make network calls, which involves these tasks:
- Include permissions in your AndroidManifest.xml file.
- On a worker thread, make an HTTP client connection that connects to the network and downloads (or uploads) data.
- Parse the results, which are usually in JSON format.
- Check the state of the network and respond accordingly.
Network security
Network transactions are inherently risky, because they involve transmitting data that could be private to the user. People are increasingly aware of these risks, especially when their devices perform network transactions, so it's very important that your app implement best practices for keeping user data secure at all times.
Security best practices for network operations:
- Use appropriate protocols for sensitive data. For example for secure web traffic, use the
HttpsURLConnection
subclass ofHttpURLConnection
. - Use HTTPS instead of HTTP anywhere that HTTPS is supported on the server, because mobile devices frequently connect on insecure networks such as public Wi-Fi hotspots. Consider using
SSLSocketClass
to implement authenticated, encrypted socket-level communication. - Don't use localhost network ports to handle sensitive interprocess communication (IPC), because other applications on the device can access these local ports. Instead, use a mechanism that lets you use authentication, for example a
Service
. - Don't trust data downloaded from HTTP or other insecure protocols. Validate input that's entered into a
WebView
and responses to intents that you issue against HTTP.
For more best practices and security tips, take a look at the Security Tips article.
Including permissions in the manifest
Before your app can make network calls, you need to include a permission in your AndroidManifest.xml file. Add the following tag inside the <manifest>
tag:
<uses-permission android:name="android.permission.INTERNET" />
When using the network, it's a best practice to monitor the network state of the device so that you don't attempt to make network calls when the network is unavailable. To access the network state of the device, your app needs an additional permission:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Performing network operations on a worker thread
Always perform network operations on a worker thread, separate from the UI. For example, in your Java code you could create an AsyncTask
(or AsyncTaskLoader
) implementation that opens a network connection and queries an API. Your main code checks whether a network connection is active. If so, it runs the AsyncTask
in a separate thread, then displays the results in the UI.
Making an HTTP connection
Most network-connected Android apps use HTTP and HTTPS to send and receive data over the network. For a refresher on HTTP, visit this Learn HTTP tutorial.
The HttpURLConnection
Android client supports HTTPS, streaming uploads and downloads, configurable timeouts, IPv6, and connection pooling. To use the HttpURLConnection
client, build a URI (the request's destination). Then obtain a connection, send the request and any request headers, download and read the response and any response headers, and disconnect.
Building your URI
To open an HTTP connection, you need to build a request URI. A URI is usually made up of a base URL and a collection of query parameters that specify the resource in question. For example to search for the first five book results for "Pride and Prejudice" in the Google Books API, use the following URI:
https://www.googleapis.com/books/v1/volumes?q=pride+prejudice&maxResults=5&printType=books
To construct a request URI programmatically, use the URI.parse()
method with the buildUpon()
and appendQueryParameter()
methods. The following code builds the complete URI shown above:
// Base URL for the Books API.
final String BOOK_BASE_URL = "https://www.googleapis.com/books/v1/volumes?";
final String QUERY_PARAM = "q"; // Parameter for the search string
final String MAX_RESULTS = "maxResults"; // Parameter to limit search results.
final String PRINT_TYPE = "printType"; // Parameter to filter by print type
// Build up the query URI, limiting results to 5 items and printed books.
Uri builtURI = Uri.parse(BOOK_BASE_URL).buildUpon()
.appendQueryParameter(QUERY_PARAM, "pride+prejudice")
.appendQueryParameter(MAX_RESULTS, "5")
.appendQueryParameter(PRINT_TYPE, "books")
.build();
To convert the URI to a string, use the toString()
method:
String myurl = builtURI.toString();
Connect and download data
In the worker thread that performs your network transactions, for example within your override of the doInBackground()
method in an AsyncTask
, use the HttpURLConnection
class to perform an HTTP GET
request and download the data your app needs. Here's how:
To obtain a new
HttpURLConnection
, callURL.openConnection()
using the URI that you've built. Cast the result toHttpURLConnection
.The URI is the primary property of the request, but request headers can also include metadata such as credentials, preferred content types, and session cookies.
- Set optional parameters:
- For a slow connection, you might want a long connection timeout (the time to make the initial connection to the resource) or read timeout (the time to actually read the data).
- To change the request method to something other than
GET
, usesetRequestMethod()
. - If you won't use the network for input, set
setDoInput
tofalse
. (Its default istrue
.) - For more methods you can set, see the
HttpURLConnection
andURLConnection
reference documentation.
- Open an input stream using
getInputStream()
, then read the response and convert it into a string. Response headers typically include metadata such as the response body's content type and length, modification dates, and session cookies. If the response has no body,getInputStream()
returns an empty stream. - Call
disconnect()
to close the connection. Disconnecting releases the resources held by a connection so they can be closed or reused.
These steps are shown in the Request example, below.
If you're posting data over the network and not just receiving data, you need to upload a request body, which holds the data to be posted. To do this:
- Configure the connection so that output is possible by calling
setDoOutput(true)
. (By default,HttpURLConnection
uses HTTPGET
requests. WhensetDoOutput
istrue
,HttpURLConnection
uses HTTPPOST
requests by default.) - Open an output stream by calling
getOutputStream()
.
For more about posting data to the network, see "Posting Content" in the HttpURLConnection documentation
.
Request example
The following example sends a request to the URL built in the Building your URI section, above. The request obtains a new HttpURLConnection
, opens an input stream, reads the response, converts the response into a string, and closes the connection.
private String downloadUrl(String myurl) throws IOException {
InputStream inputStream = null;
// Only display the first 500 characters of the retrieved
// web page content.
int len = 500;
try {
URL url = new URL(myurl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */);
// Start the query
conn.connect();
int response = conn.getResponseCode();
Log.d(DEBUG_TAG, "The response is: " + response);
inputStream = conn.getInputStream();
// Convert the InputStream into a string
String contentAsString = convertInputToString(inputStream, len);
return contentAsString;
// Close the InputStream and connection
} finally {
conn.disconnect();
if (inputStream != null) {
inputStream.close();
}
}
}
Converting the InputStream to a string
An InputStream
is a readable source of bytes. Once you get an InputStream
, it's common to decode or convert it into the data type you need. In the example above, the InputStream
represents plain text from the web page located at https://www.googleapis.com/books/v1/volumes?q=pride+prejudice&maxResults=5&printType=books.
The convertInputToString
method defined below converts the InputStream
to a string so that the activity can display it in the UI. The method uses an InputStreamReader
instance to read bytes and decode them into characters:
// Reads an InputStream and converts it to a String.
public String convertInputToString(InputStream stream, int len)
throws IOException, UnsupportedEncodingException {
Reader reader = null;
reader = new InputStreamReader(stream, "UTF-8");
char[] buffer = new char[len];
reader.read(buffer);
return new String(buffer);
}
InputStreamReader
inside a BufferedReader
for more efficient reading of characters, arrays, and lines. For example: reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
Parsing the results
When you make web API queries, the results are often in JSON format.
Below is an example of a JSON response from an HTTP request. It shows the names of three menu items in a popup menu and the methods that are triggered when the menu items are clicked:
{"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}
To find the value of an item in the response, use methods from the JSONObject
and JSONArray
classes. For example, here's how to find the "onclick"
value of the third item in the "menuitem"
array:
JSONObject data = new JSONObject(responseString);
JSONArray menuItemArray = data.getJSONArray("menuitem");
JSONObject thirdItem = menuItemArray.getJSONObject(2);
String onClick = thirdItem.getString("onclick");
Managing the network state
Making network calls can be expensive and slow, especially if the device has little connectivity. Being aware of the network connection state can prevent your app from attempting to make network calls when the network isn't available.
Sometimes it's also important for your app to know what kind of connectivity the device has: Wi-Fi networks are typically faster than data networks, and data networks are often metered and expensive. To control when certain tasks are performed, monitor the network state and respond appropriately. For example, you may want to wait until the device is connected to Wi-Fi to perform a large file download.
To check the network connection, use the following classes:
ConnectivityManager
answers queries about the state of network connectivity. It also notifies applications when network connectivity changes.NetworkInfo
describes the status of a network interface of a given type (currently either mobile or Wi-Fi).
The following code snippet tests whether Wi-Fi and mobile are connected. In the code:
- The
getSystemService
method gets an instance ofConnectivityManager
. - The
getNetworkInfo
method gets the status of the device's Wi-Fi connection, then its mobile connection. ThegetNetworkInfo
method returns aNetworkInfo
object, which contains information about the given network's connection status (whether it's idle, connecting, and so on). - The
networkInfo.isConnected()
method returnstrue
if the given network is connected. If the network is connected, it can be used to establish sockets and pass data.private static final String DEBUG_TAG = "NetworkStatusExample"; ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI); boolean isWifiConn = networkInfo.isConnected(); networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); boolean isMobileConn = networkInfo.isConnected(); Log.d(DEBUG_TAG, "Wifi connected: " + isWifiConn); Log.d(DEBUG_TAG, "Mobile connected: " + isMobileConn);
Related practical
The related exercises and practical documentation is in Android Developer Fundamentals: Practicals.